home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / mac / hypercrd / xcmd / dxcmds34.sit / Dartmouth XCMD's 3.4.3 / card_10251.txt < prev    next >
Text File  |  1990-04-17  |  30KB  |  967 lines

  1. -- card: 10251 from stack: in.3
  2. -- bmap block id: 16119
  3. -- flags: 4000
  4. -- background id: 16896
  5. -- name: MultiSort
  6. ----- HyperTalk script -----
  7. on opencard
  8.   global nextPos
  9.   hide card field "source"
  10.   set the scroll of card field "documentation" to 0
  11.   hide button "arrow"
  12.   put 0 into nextPos
  13.   set the loc of btn arrow to "312,40"
  14.   put empty into card field "sorted"
  15.   pass opencard
  16. end opencard
  17.  
  18. on Install
  19.   get ChooseTargetStack()
  20.   InstallResource XFCN,MultiSort,it
  21. end Install
  22.  
  23.  
  24. -- part 15 (field)
  25. -- low flags: 01
  26. -- high flags: 0004
  27. -- rect: left=322 top=199 right=302 bottom=485
  28. -- title width / last selected line: 0
  29. -- icon id / first selected line: 0 / 0
  30. -- text alignment: 0
  31. -- font id: 3
  32. -- text size: 9
  33. -- style flags: 0
  34. -- line height: 12
  35. -- part name: sorted
  36.  
  37.  
  38. -- part 6 (button)
  39. -- low flags: 00
  40. -- high flags: 8003
  41. -- rect: left=304 top=303 right=325 bottom=425
  42. -- title width / last selected line: 0
  43. -- icon id / first selected line: 0 / 0
  44. -- text alignment: 1
  45. -- font id: 0
  46. -- text size: 12
  47. -- style flags: 0
  48. -- line height: 16
  49. -- part name: Show C Source
  50. ----- HyperTalk script -----
  51. on mouseUp
  52.   get the visible of card field "source"
  53.   set the visible of card field "source" to not it
  54.   if it is false then
  55.     set the name of me to "Hide C Source"
  56.   else
  57.     set the name of me to "Show C Source"
  58.   end if
  59. end mouseUp
  60.  
  61.  
  62.  
  63. -- part 9 (field)
  64. -- low flags: 01
  65. -- high flags: 2007
  66. -- rect: left=18 top=33 right=287 bottom=295
  67. -- title width / last selected line: 0
  68. -- icon id / first selected line: 0 / 0
  69. -- text alignment: 0
  70. -- font id: 3
  71. -- text size: 9
  72. -- style flags: 0
  73. -- line height: 12
  74. -- part name: Documentation
  75.  
  76.  
  77. -- part 11 (button)
  78. -- low flags: 80
  79. -- high flags: 0000
  80. -- rect: left=303 top=29 right=51 bottom=321
  81. -- title width / last selected line: 0
  82. -- icon id / first selected line: 16560 / 16560
  83. -- text alignment: 1
  84. -- font id: 0
  85. -- text size: 12
  86. -- style flags: 0
  87. -- line height: 16
  88. -- part name: arrow
  89.  
  90.  
  91. -- part 13 (field)
  92. -- low flags: 01
  93. -- high flags: 0004
  94. -- rect: left=322 top=39 right=142 bottom=485
  95. -- title width / last selected line: 0
  96. -- icon id / first selected line: 0 / 0
  97. -- text alignment: 0
  98. -- font id: 3
  99. -- text size: 9
  100. -- style flags: 0
  101. -- line height: 12
  102. -- part name: data
  103.  
  104.  
  105. -- part 14 (field)
  106. -- low flags: 01
  107. -- high flags: 0004
  108. -- rect: left=322 top=151 right=191 bottom=487
  109. -- title width / last selected line: 0
  110. -- icon id / first selected line: 0 / 0
  111. -- text alignment: 0
  112. -- font id: 3
  113. -- text size: 9
  114. -- style flags: 0
  115. -- line height: 12
  116. -- part name: spec
  117.  
  118.  
  119. -- part 7 (field)
  120. -- low flags: 81
  121. -- high flags: 0007
  122. -- rect: left=18 top=31 right=290 bottom=489
  123. -- title width / last selected line: 0
  124. -- icon id / first selected line: 0 / 0
  125. -- text alignment: 0
  126. -- font id: 3
  127. -- text size: 10
  128. -- style flags: 0
  129. -- line height: 13
  130. -- part name: Source
  131.  
  132.  
  133. -- part 12 (button)
  134. -- low flags: 00
  135. -- high flags: A003
  136. -- rect: left=85 top=298 right=320 bottom=185
  137. -- title width / last selected line: 0
  138. -- icon id / first selected line: 0 / 0
  139. -- text alignment: 1
  140. -- font id: 0
  141. -- text size: 12
  142. -- style flags: 0
  143. -- line height: 16
  144. -- part name: Sort
  145. ----- HyperTalk script -----
  146. on mouseUp
  147.   put MultiSort(card field "data",card field "spec") into card field "sorted"
  148. end mouseUp
  149.  
  150.  
  151.  
  152. -- part contents for card part 7
  153. ----- text -----
  154. /* MultiSort1.0d4.c */
  155. /* ┬⌐ Trustees of Dartmouth College */
  156. /* written in LightSpeed C  ┬⌐ Think Technologies, Inc */
  157. /* by Roger Brown 9/2/89  Courseware Development Group */
  158.  
  159. /* version 1.0d4 uses international string comparison */
  160. /* version 1.0d3 first source release */
  161. /* version 1.0d2: clean up the code, change type spec to whole words. */
  162. /* version 1.0d1: numbers are handled in floating point */
  163. /* Distribution version. Source is for THINK C version 4.0, but I still use
  164.    the qksort routine from THINK LSCΓäó v. 3.0 because the v 4.0 _qsort
  165.    doesn't seem to work. */
  166.  
  167. /*  This is a HyperCard XFCN that sorts the lines of a HyperCard container
  168.    according to the value of specified items in the line. It will do
  169.    a multilevel sort on several items and can sort ascending or descending
  170.    by alphabetic, numeric, time, or date order.
  171.     There is no limit to the number of lines except for available memory.
  172.     It uses the Lightspeed C quicksort function and it is quite fast! Only 
  173.     one pass is made over the data to find line starts.
  174.     Case is ignored in this version.
  175.          
  176.      Syntax is:
  177.    
  178.               get MultiSort(container,specification)
  179.    
  180.              where    container is any hypercard container (field, variable), 
  181.                           presumed to be multi-lined.
  182.                        specification is a container with this format:
  183.                           for each item to sort on, in priority order,
  184.                           specify one line of three items: item number to sort on,
  185.                           sort type (alpha,numeric,date,time), and ascending or 
  186.                           decending (a,d)
  187.  
  188.              returns: a copy of the container sorted by line
  189.                            or error messages.
  190.             
  191.              ex.      get MultiSort(card field 1,spec)
  192.              
  193.                 where spec is:
  194.                 2,alpha,a         item 2, alpahbetic order, ascending
  195.                 3,numeric,d       then item 3, numeric, descending
  196.                 
  197.       Note on data formats:
  198.           Time items must be in format hh:mm:ss (ex. 10:15:02). Seconds and hours are
  199.           optional.  MultiSort WILL NOT report improperly formatted times and
  200.           WILL NOT correct for mis-specified time (ex. 9:99 the way that
  201.           HyperCard can. If you are uncertain of time data, convert it to seconds
  202.           in HyperTalk and sort it numerically.
  203.        
  204.          Date items must be in format mm/dd/yy (ex. 2/15/78). All three parts must
  205.          be specified.  MultiSort WILL NOT report improperly formatted dates and
  206.           WILL NOT correct for mis-specified dates (ex. 1/44/50) the way that
  207.           HyperCard can. If you are uncertain of date data, convert it to seconds
  208.           in HyperTalk and sort it numerically.
  209.           
  210.                  
  211.       To compile: create a project with this, MacTraps, and the THINK LightSpeed CΓäó
  212.       version 3.0 library source called qsort.c.  (The THINK CΓäó version 4.0 code
  213.       does not work with this XFCN in its present state.) and a file called 
  214.       C Utilities.c that you can generate from the C Utilities card of Dartmouth XCMD's. 
  215.       Build as code resource type XFCN named MultiSort, ID 2351.
  216. */
  217.  
  218. #include "MemoryMgr.h"
  219. #include "HyperXCmd.h"
  220. #include "sane.h"
  221. #include "XCmdGlue.inc.c"
  222. #include "SetUpA4.h"
  223.  
  224. #define NULL 0L
  225. #define maxItems 32
  226. #define alpha     1
  227. #define numeric   2
  228. #define time  3
  229. #define date      4
  230. #define ascend    1
  231. #define descend   2
  232.  
  233. #define slash '/'
  234. #define colon ':'
  235.  
  236. #define memoryError -1            /* marks a memory allocation error */
  237. int *order;                       /* points to the line order array (in heap) */
  238. Handle orderHandle;               /* handle to the line order array */ 
  239. int *lineStarts;                  /* points to array of line starts (in heap) */
  240. Handle lsHandle;                  /* handle to the line starts array */      
  241. Handle theField;                  /* handle to the input copy of the container */
  242. Handle theUCASEField;             /* handle to an upper case version of container */
  243. Handle DoSort();                  /* the sorting routine */
  244. int numSortFields;                /* number of fields to sort on */
  245. long sortItem[maxItems];          /* the number of the sort items */
  246. char sortType[maxItems];          /* how to sort */
  247. char sortDirection[maxItems];     /* which direction to sort */
  248. long MyDateToSecs();
  249.  
  250.  
  251. /* ------------------- utility functions ---------------------- */
  252.  
  253. /* change a string to all upper case */
  254.  
  255. ucase(s)
  256. char *s;
  257. {
  258. int i;
  259. char c;
  260.  
  261. for (i=0;i<strlen(s);i++) {
  262. s[i] = toupper(s[i]);
  263. }
  264. }
  265.  
  266. /* Get the number of HyperCard comma delimited items in string s. */
  267.  
  268. int NumHCItems(s)
  269. char *s;
  270. {
  271. int c;                                           /* character pointer */
  272. int len;                                         /* length of string */
  273. int count;                                       /* count of items found */
  274. int j;                                           /* item byte counter */  
  275.  
  276. count = j = 0;                            /* initialize */
  277. len = strlen(s);                                 /* will look this far */
  278. for (c=0;c<len;c++) {                            /* scan all characters */
  279. if (s[c]==',') {                             /* found end of an item */
  280. count = count + 1;                       /* bump item found counter */
  281. j = 0;                                   /* reset item scan counter */
  282. }
  283. else {                                       
  284. j++;                                     /* bump item byte counter */
  285. if (c==(len-1)) {                        /* last item, no comma */
  286. count = count+1;                     /* this one counts, too */
  287. break;
  288. }
  289. }
  290. }
  291. return count;
  292. }
  293.  
  294. /* Get HyperCard comma delimited item i from item list string inStr. Return it in outStr */
  295. /* item must be smaller than 255 characters */
  296.  
  297. GetHCItem(inStr,i,outStr)
  298. char *inStr,*outStr;
  299. int i;
  300. {
  301. int c;                                           /* character pointer */
  302. int len;                                         /* length to scan */
  303. int count;                                       /* count of items found */
  304. int j;                                           /* item byte count */
  305. Str255 temp;                                     /* collect item here, hope its < 255 */
  306.  
  307. count = j = 0;                                   /* initialize */
  308. len = strlen(inStr);                             /* go this far, max */
  309. for (c=0;c<len;c++) {
  310. if (inStr[c]==',') {                         /* at an item boundary */
  311. count = count + 1;                       /* bump counter */
  312. if (count==i) break;                     /* if this is it, stop scanning */
  313. j = 0;                                   /* else start on the next item */
  314. }
  315. else {
  316. temp[j] = inStr[c];                      /* collect characters to return */
  317. j++;                                     /* go to next */
  318. if (j==255) {                            /* sorry, too big to store */
  319. strcpy(temp,"Error: Item > 255 characters.");
  320. return;                       
  321. }
  322. if (c==(len-1)) {                        /* last item, no comma */
  323. count = count+1;
  324. break;
  325. }
  326. }
  327. }
  328. if (count < i) strcpy(outStr,"Error: item out of range");   /* no item there */
  329. else {
  330. temp[j] = 0;                                     /* make it a C string */
  331. strcpy(outStr,temp);                             /* move to output string */
  332. }
  333. return;
  334. }
  335.  
  336. /* how many HyperCard lines in string source  */
  337.  
  338. NumHCLines(source) 
  339. char *source;
  340. {
  341. int c;                                           /* character counter */
  342. int l;                                           /* line counter */
  343. int len;
  344.  
  345. len = strlen(source);
  346. l = 0;
  347. for (c=0;c<len;c++) {                            /* scan to desired line */
  348. if (source[c]==13) l++;
  349. }
  350. if (source[c-1]!=13) l++;                        /* last line might not have CR */
  351. return l;
  352.  
  353. }
  354. /* get HyperCard line from source and return in dest */
  355.  
  356. GetHCLine(line,source,dest)
  357. int line;
  358. char *source,*dest;
  359. {
  360. int i;                                                /* char counter before line */
  361. int j;                                                /* char cou,tner after line */
  362. int c;                                                /* line counter */
  363. int len;                                              /* length of source */
  364.  
  365. len = strlen(source);                                 /* go this far, max */
  366. c = 1;                                                /* initialize */
  367. i = 0;
  368. while (c<line) {                                      /* cycle to desired line */
  369. if (source[i]==13) c++;                           /* bump line count */
  370. i++;
  371. if (i>len) {                                      /* out of range */
  372. strcpy(dest,"Error: Line out of range."); 
  373. return;
  374. }
  375. }
  376. c = 0;                                                /* at line, start transfer */
  377. for (j=i;j<len;j++) {
  378. *(dest+c) = source[j];
  379. if (source[j]==13) break;                         /* line ended */
  380. c++;
  381. }
  382. *(dest+c) = (char)0;                                  /* make into a C string */
  383. }
  384.  
  385. /* build a return result structure from a string */
  386.  
  387. ResultIs(paramPtr,theResult)
  388. XCmdBlockPtrparamPtr;
  389. char *theResult;
  390. {
  391. long len;
  392. Handle resultHandle;
  393. len = 1+strlen(theResult);
  394. resultHandle = NewHandle(len);
  395. BlockMove(theResult,*resultHandle,len);
  396. paramPtr->returnValue = resultHandle;
  397. }
  398.  
  399. /* check string for valid ascii and convert if ok. Return validity.
  400.    Input string is changed to Pascal format */
  401.  
  402. char ValidStrToNum(s,n)
  403. char *s;
  404. long *n; 
  405. {
  406. int c,len;
  407.  
  408. /* length must be < 32768 */
  409. len = strlen(s);
  410. if ((len<1)||(len>32768)) return FALSE; /* all characters must be 0..9,-,+ */
  411. for (c=0;c<len;c++) {
  412.    if ((s[c]<48)||(s[c]>57))
  413.       if ((s[c]!=45)&&(s[c]!=43)) return FALSE;
  414. }
  415. CtoPstr(s);
  416. StringToNum(s,n);
  417. return TRUE;
  418. }
  419.  
  420. /* Get part of string "to" from characters "start" to "stop".  Return it in 
  421.    string "from" */
  422.       
  423. SubString(to,from,start,stop)
  424. char *to,*from;
  425. int start,stop;
  426. {
  427. int i,j;
  428. j = 0;
  429. for (i=start;i<stop;i++,j++) {
  430. to[j] = from[i];
  431. }
  432. to[j] = 0;
  433. }
  434.  
  435. /* Determine the integer value of a number expressed in a C string. */
  436.  
  437. CStrToInt(str)
  438. char *str;
  439. {
  440. long n;
  441.  
  442. CtoPstr((char*)str);
  443. StringToNum(str,&n);
  444. return (int)n;
  445. }
  446.  
  447. /* Determine the extended value of a number expressed in a C string. */
  448.  
  449. extended CStrToExtended(s)
  450. char *s;
  451. {
  452. CtoPstr((char*)s);
  453. return str2num(s);
  454. }
  455.  
  456. /* -------------------- program code ---------------------- */
  457.  
  458. /* Get all line starts in one sweep, changing CRs to 0 bytes so that
  459.     I have real C strings to work with later. Also process an upper case 
  460.     copy of the input data by changing CRs to 0s and upper casing 
  461.     everthing else. We process both copies because we want to sort the
  462.     upper case version but reassemble the result from the original. 
  463.    
  464.     Returns the number of lines found.
  465. */
  466.    
  467. GetLineStarts(source,ucSource)
  468. char *source,*ucSource;    /* the data source and a copy to ucase */
  469. {
  470.         int c;/* character counter */
  471.         int l;/* line counter */
  472.         long startsSize;/* size of the line starts array */
  473.         
  474.         /* allocate some memry to hold the line starts "array" */
  475.         startsSize = 128;              /* arbitrary starting size */
  476.         lsHandle = NewHandle(startsSize*2);
  477.         if (lsHandle==NULL) {          /* handle error */
  478.                 SysBeep(60);
  479.                 return memoryError;
  480.         }
  481.         HLock(lsHandle);                  
  482.         lineStarts = (int *)*lsHandle;
  483.         
  484.         /* scan the input data to find line starts */
  485.         l = c = 0;
  486.         *lineStarts = 0;                     /* first line starts at byte 0 */
  487.         while (source[c] != 0) {               /* scan to end */
  488.                 if (source[c]==13) {            /* at end of a line */
  489.                         l++;
  490.                         *(lineStarts+l) = c+1;      /* next one starts here */
  491.                         source[c] = 0;               /* change cr to 0 */
  492.                         ucSource[c] = 0;           /* and do same to the ucase version */
  493.                         if (l==startsSize-10) {     /* need more room for line starts */
  494.                                 startsSize = startsSize + 128;
  495.                                 HUnlock(lsHandle);
  496.                                 SetHandleSize(lsHandle,startsSize*2);
  497.                                 if (lsHandle==NULL) {    /* oops, out of room */
  498.                                         SysBeep(60);
  499.                                         return memoryError;
  500.                                 }
  501.                                 HLock(lsHandle);
  502.                                 lineStarts = (int *)*lsHandle;  /* it might have moved */
  503.                         }
  504.                 }
  505.                 ucSource[c] = toupper(ucSource[c]);  /* change to ucase in line */
  506.                 c++;
  507.         }
  508.         if (source[c-1]==0) l--;           /* last line might not have CR */
  509.         return l+1;                         
  510. }
  511.  
  512.  
  513. /* compare two numbers, return -1 if the first is larger, 1 if the second is larger
  514.    and 0 if they are equal */
  515.    
  516. CompareNums(n1,n2)
  517. char *n1,*n2;
  518. {
  519. char t1[64],t2[64];
  520. extended num1,num2;
  521.  
  522. strcpy(t1,n1);
  523. strcpy(t2,n2);
  524. num1 = CStrToExtended(t1);
  525. num2 = CStrToExtended(t2);
  526. if (num1 < num2) return -1;
  527. else if (num1 > num2) return 1;
  528. return 0;
  529. }
  530.  
  531. /* Change a date in character format into a DateTimeRec format. */
  532.  
  533. ParseDate(tStr,dtr)
  534. char *tStr;
  535. DateTimeRec *dtr;
  536. {
  537. /* Input must be in mm/dd/yy format */
  538.  
  539. int delim[3],next = 0;
  540. char monthStr[16],dayStr[16],yearStr[16];
  541. char *p;
  542.  
  543. delim[0] = delim[1] = delim[2] = 0;
  544. p = tStr;
  545. while (1) {                  /* find the '/' delimeters */
  546. if ((*p==slash)||(*p=='\0')) {
  547. delim[next] = (p-tStr);
  548. next++;
  549. }
  550. if (*p == '\0') break;
  551. p++;
  552. }
  553. SubString(monthStr,tStr,0,delim[0]);           /* get the 3 parts */
  554. SubString(dayStr,tStr,delim[0]+1,delim[1]);
  555. SubString(yearStr,tStr,delim[1]+1,delim[2]);
  556. dtr->month = CStrToInt(monthStr);              /* convert */
  557. dtr->day = CStrToInt(dayStr);
  558. dtr->year = CStrToInt(yearStr);
  559. }
  560.  
  561. /* Compare two dates in DateTimeRec format.  Return -1 if the first is later,
  562.    0 if they are equal, and 1 if the second is later. */
  563.    
  564. CompareDates(d1,d2)
  565. char *d1,*d2;
  566. {
  567. DateTimeRec dtr[2];
  568.  
  569. ParseDate(d1,&dtr[0]);
  570. ParseDate(d2,&dtr[1]);
  571.  
  572. if (dtr[0].year > dtr[1].year) return 1;
  573. else if (dtr[0].year < dtr[1].year) return -1;
  574. if (dtr[0].month > dtr[1].month) return 1;
  575. else if (dtr[0].month < dtr[1].month) return -1;
  576. if (dtr[0].day > dtr[1].day) return 1;
  577. else if (dtr[0].day < dtr[1].day) return -1;
  578. return 0;
  579. }
  580.  
  581. /* Change a time in character format into a DateTimeRec format. */
  582.  
  583. ParseTime(tStr,dtr)
  584. char *tStr;
  585. DateTimeRec *dtr;
  586. {
  587. /* Time must be in hh:mm:ss format. */
  588.  
  589. int delim[3],next = 0;
  590. char hourStr[16],minStr[16],secStr[16];
  591. char *p;
  592.  
  593. delim[0] = delim[1] = delim[2] = 0;    /* find the ":" delimeters */
  594. p = tStr;
  595. while (1) {
  596. if ((*p==colon)||(*p=='\0')) {
  597. delim[next] = (p-tStr);
  598. next++;
  599. }
  600. if (*p == '\0') break;
  601. p++;
  602. }   
  603. SubString(hourStr,tStr,0,delim[0]);         /* get the parts */
  604. SubString(minStr,tStr,delim[0]+1,delim[1]);
  605. SubString(secStr,tStr,delim[1]+1,delim[2]);
  606. dtr->hour = CStrToInt(hourStr);              /* convert */
  607. dtr->minute = CStrToInt(minStr);
  608. dtr->second = CStrToInt(secStr);
  609. }
  610.  
  611. /* Compare two tiems in DateTimeRec format.  Return -1 if the first is later,
  612.    0 if they are equal, and 1 if the second is later. */
  613.  
  614. CompareTimes(t1,t2)
  615. char *t1,*t2;
  616. {
  617. DateTimeRec dtr[2];
  618.  
  619. ParseTime(t1,&dtr[0]);
  620. ParseTime(t2,&dtr[1]);
  621.  
  622. if (dtr[0].hour > dtr[1].hour) return 1;
  623. else if (dtr[0].hour < dtr[1].hour) return -1;
  624. if (dtr[0].minute > dtr[1].minute) return 1;
  625. else if (dtr[0].minute < dtr[1].minute) return -1;
  626. if (dtr[0].second > dtr[1].second) return 1;
  627. else if (dtr[0].second < dtr[1].second) return -1;
  628. return 0;
  629. }
  630.  
  631. /* compare two fields (items). Comparison depends on field type and the
  632.    result is adjusted for ascending or descending specification */
  633.    
  634. CompareFields(f1,f2,type,dir) 
  635. char *f1,*f2;                      /* string value of the 2 fields */
  636. char type,dir;                     /* field type and direction */
  637. {
  638. int result;
  639. int len1,len2;
  640.  
  641. switch (type) {
  642. case alpha:
  643. len1 = strlen(f1);
  644. len2 = strlen(f2);
  645. result = IUMagString(f1,f2,len1,len2);
  646. break;
  647. case numeric:
  648. result = CompareNums(f1,f2);
  649. break;
  650. case date:
  651. result = CompareDates(f1,f2);
  652. break;
  653. case time:
  654. result = CompareDates(f1,f2);
  655. break;
  656. }
  657. if (dir == descend) 
  658. result = -1*result;
  659. return result;
  660. }
  661.  
  662. /* Compare line i and line j. This function is an input to qsort. */
  663.  
  664. static int compare(i,j)
  665. int i,j;
  666. {
  667. int f,result;
  668. Str255 item1,item2;
  669.  
  670. for (f=0;f<numSortFields;f++) {        /* scan all sort fields */
  671. GetHCItem(*theUCASEField+*(lineStarts+order[i]),(int)sortItem[f],item1);
  672.   GetHCItem(*theUCASEField+*(lineStarts+order[j]),(int)sortItem[f],item2);
  673.   result = CompareFields(item1,item2,sortType[f],sortDirection[f]);
  674.   if (result!=0) {
  675.   return result;  
  676.   }  
  677.   }
  678.   return 0;           /* lines match */
  679. }
  680.  
  681. /* Swap line i with line j. This function is an input to qsort. */
  682.  
  683. static void swap(i,j)
  684. int i,j;
  685. {
  686.         int swap;
  687.         swap = order[i];             /* We don't swap them in place, just */
  688.         order[i] = order[j];         /* their positions in the order array */
  689.         order[j] = swap;
  690. }
  691.  
  692. /* Do the sort and return a handle to the sorted result */
  693.  
  694. Handle DoSort()
  695. {
  696.         Handle tempField;          /* handle to memory to build the result */
  697.         int i,j,next;
  698.         int numLines;
  699.         Str255 temp1;
  700.         OSErr err;
  701.         long orderSize;
  702.         long test;
  703.         char c;
  704.         int sLen,last;
  705.         
  706.         /* make a copy of the data to convert to upper case */
  707.         
  708.         theUCASEField = theField;
  709.         err = HandToHand(&theUCASEField);   
  710.         if (err != noErr) {                      /* some memory problem */
  711.                 tempField = NewHandle(32);       /* hope it fits */
  712.                 strcpy(*tempField,"Not enough memory to sort (1)");
  713.                 return tempField;
  714.         }
  715.         HLock(theUCASEField);                   /* lock these down so we use pointers to them */
  716.         HLock(theField);
  717.         
  718.         /* get line starts, number of lines, and an uppercase copy */
  719.         
  720.         numLines = GetLineStarts(*theField,*theUCASEField);
  721.         if (numLines == memoryError) {    /* some memory problem */
  722.                 tempField = NewHandle(32); 
  723.                 strcpy(*tempField,"Not enough memory to sort (2)");
  724.                 return tempField;
  725.         }
  726.         
  727.         /* allocate space for the order array */
  728.         
  729.         orderSize = (numLines+1)*2;                /* this is an integer array */
  730.         orderHandle = NewHandle(orderSize);
  731.         if (orderHandle==NULL) {                   /* some memory problem */
  732.                 tempField = NewHandle(32); 
  733.                 strcpy(*tempField,"Not enough memory to sort (3)");
  734.                 return tempField;
  735.         }
  736.         
  737.         /* lock it down so we can use a pointer to it */
  738.         
  739.         HLock(orderHandle);
  740.         order = (int *)*orderHandle;
  741.         
  742.          /* initialize the order list */
  743.          
  744.         for (i=0;i<numLines;i++)
  745.                 order[i] = i;    
  746.         
  747.         /* let THINK C qksort do the hard work for us */
  748.         
  749.         qksort(numLines,compare,swap);
  750.         
  751.         /* Now build the sorted result: grab each line in the new order
  752.              as a C string and concatenate it to the result string. */
  753.         
  754.         tempField = theUCASEField;          /* resuse this memory, its the right size */
  755.         next = **tempField = 0;
  756.         for (i=0;i<numLines;i++) {
  757.                 sLen = 1+strlen(*theField+*(lineStarts+order[i]));
  758.                 for (j=0;j<sLen;j++,next++) {
  759.                         c = *(*theField+*(lineStarts+order[i])+j);
  760.                         if (c==0) c = '\15';      /* change 0s back to CRs */
  761.                         *(*(tempField)+next) = c;
  762.                 }
  763.         }
  764.         next--;
  765.         *(*(tempField)+next) = 0;         /* put a 0 terminator on the result */
  766.  
  767.         /* clean up */
  768.         
  769.         DisposHandle(orderHandle);
  770.         DisposHandle(lsHandle);
  771.         
  772.         /* return the result */
  773.         
  774.         return tempField;
  775. }
  776.  
  777.  
  778. /* XFCN entry point */
  779.  
  780. pascal void main(paramPtr)
  781. XCmdBlockPtr    paramPtr;
  782. {
  783.         Str255 paramStr,lineStr,itemStr;
  784.         int numItems,i,j;
  785.         char c;
  786.         
  787.         /* Prepare to use globals */
  788.         
  789.         RememberA0();
  790.         SetUpA4();
  791.         
  792.         /* check parameter count */
  793.         
  794.         if (paramPtr->paramCount != 2) {
  795.         strcpy(paramStr,"Not enough parameters in MultiSort.");
  796.         ResultIs(paramPtr,paramStr);
  797.         RestoreA4();
  798.         return;
  799.         }
  800.         
  801.         /* get the input container copy */
  802.         
  803.         MoveHHi(paramPtr->params[0]);
  804.         HLock(paramPtr->params[0]);
  805.         theField = (Handle)paramPtr->params[0];
  806.        
  807.        /* get the specification container */
  808.        
  809.         HLock(paramPtr->params[1]);
  810.         strcpy(paramStr,*(paramPtr->params[1]));
  811.         
  812.         /* check bounds on sort items */
  813.         numSortFields = NumHCLines(paramStr);
  814.         if (numSortFields > maxItems) {
  815.         strcpy(paramStr,"MultiSort cannot handle more than 32 sort items.");
  816.         ResultIs(paramPtr,paramStr);
  817.         RestoreA4();
  818.         return;
  819.         }
  820.         
  821.         /* check each specification line and remember the specs */
  822.         
  823.         for (i=0;i<numSortFields;i++) {
  824.         GetHCLine(i+1,paramStr,lineStr);
  825.         numItems = NumHCItems(lineStr);
  826.         if (numItems != 3) {
  827.         strcpy(paramStr,"Not enough parameters in sort specification line: ");
  828.         NumToString((long)i+1,lineStr);
  829.         PtoCstr((char*)lineStr);
  830.         strcat(paramStr,lineStr);
  831.         ResultIs(paramPtr,paramStr);
  832.         RestoreA4();
  833.         return;
  834.         }
  835.         for (j=0;j<3;j++) {
  836.         GetHCItem(lineStr,j+1,itemStr);
  837.         switch (j) {
  838.         case 0:
  839.         if (ValidStrToNum(itemStr,&sortItem[i])!=TRUE) {
  840.         strcpy(paramStr,"Sort item must be a number in line: ");
  841.         NumToString((long)j+1,lineStr);
  842.         PtoCstr((char*)lineStr);
  843.         strcat(paramStr,lineStr);
  844.         ResultIs(paramPtr,paramStr);
  845.         RestoreA4();
  846.         return;
  847.         }
  848.         break;
  849.         case 1:
  850.         ucase(itemStr);
  851.         if (strcmp(itemStr,"NUMERIC")==0) sortType[i] = numeric;
  852.         else if (strcmp(itemStr,"DATE")==0) sortType[i] = date;
  853.         else if (strcmp(itemStr,"TIME")==0) sortType[i] = time;
  854.         else sortType[i] = alpha;    /* default */
  855.         break;
  856.         case 2:
  857.         ucase(itemStr);
  858.         c = itemStr[0];
  859.         switch (c) {
  860.         case 'A': sortDirection[i] = ascend;break;
  861.         case 'D': sortDirection[i] = descend;break;
  862.         default:  sortDirection[i] = ascend;break;
  863.        }
  864.        break;
  865.         }
  866.         }
  867.         }
  868.         
  869.         /* do the sort */
  870.         paramPtr->returnValue = DoSort(numSortFields);
  871.          
  872.         /* clean up */
  873.         HUnlock (paramPtr->params[0]);
  874.         HUnlock (paramPtr->params[1]);
  875.         RestoreA4(); 
  876.  return;
  877. }
  878.  
  879.  
  880.  
  881. -- part contents for card part 9
  882. ----- text -----
  883. MultiSort version 1.0d4
  884. Roger Brown
  885.  
  886. This is a HyperCard XFCN that sorts the lines of a HyperCard container according to the value of specified items in the line. It will do a multilevel sort on several items and can sort ascending or descending
  887. by alphabetic, numeric, time, or date order.
  888.     There is no limit to the number of lines except for available memory. It uses the Lightspeed C quicksort function and it is quite fast! Only one pass is made over the data to find line starts.
  889.     Case is ignored in this version.
  890.          
  891.      Syntax is:
  892.    
  893.               get MultiSort(container,specification)
  894.    
  895.              where    container is any hypercard container 
  896.                           (field, variable),  presumed to be 
  897.                           multi-lined.
  898.                           specification is a container with this 
  899.                           format:
  900.                           for each item to sort on, in priority 
  901.                           order, specify one line of three 
  902.                           items: item number to sort on,
  903.                           sort type (alpha,numeric,date,time),   
  904.                           and ascending or descending (a,d)
  905.  
  906.              returns: a copy of the container sorted by 
  907.                            line or error messages.
  908.             
  909.              ex.      get MultiSort(card field 1,spec)
  910.              
  911.                 where spec is:
  912.                 2,alpha,a         (item 2, alphabetic order,          
  913.                                          ascending)
  914.                 3,numeric,d     (then item 3, numeric, 
  915.                                          descending)
  916.                 
  917.       Note on data formats:
  918.           Time items must be in format hh:mm:ss (ex. 
  919.           10:15:02). Seconds and hours are optional.  
  920.           MultiSort WILL NOT report improperly 
  921.           formatted times and WILL NOT correct for mis-
  922.           specified time (ex. 9:99 the way that
  923.           HyperCard can. If you are uncertain of time 
  924.           data, convert it to seconds in HyperTalk and 
  925.           sort it numerically.
  926.        
  927.          Date items must be in format mm/dd/yy 
  928.          (ex. 2/15/78). All three parts must be 
  929.          specified.  MultiSort WILL NOT report 
  930.          improperly formatted dates and WILL NOT 
  931.          correct for mis-specified dates (ex. 1/44/50) 
  932.          the way that HyperCard can. If you are 
  933.          uncertain of date data, convert it to seconds
  934.           in HyperTalk and sort it numerically.
  935.  
  936. REVISION HISTORY
  937. 1.0d3 First public release. 
  938. 1.0d4 uses international string comparison for alpha 
  939.           sorting.
  940.  
  941. -- part contents for card part 13
  942. ----- text -----
  943. Ford, red, 1975, 4000
  944. Chevy,red,1975,3500
  945. Ford,white,1980,6000
  946. Ford,red,1980,6500
  947. Chevy,white,1985,8000
  948. Buick,blue,1974,2000
  949. Buick,blue,1974,2400
  950. Ford,red,1980,6000
  951.  
  952. -- part contents for card part 14
  953. ----- text -----
  954. 1,alpha,a
  955. 3,date,a
  956. 4,numeric,d
  957.  
  958. -- part contents for card part 15
  959. ----- text -----
  960. Buick,blue,1974,2400
  961. Buick,blue,1974,2000
  962. Chevy,red,1975,3500
  963. Chevy,white,1985,8000
  964. Ford, red, 1975, 4000
  965. Ford,red,1980,6500
  966. Ford,red,1980,6000
  967. Ford,white,1980,6000